package com.chaoyue.media.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chaoyue.base.constant.MediaProcessStatus;
import com.chaoyue.base.constant.MediaStatus;
import com.chaoyue.base.exception.ChaoYuePlusException;
import com.chaoyue.base.model.PageParams;
import com.chaoyue.base.model.PageResult;
import com.chaoyue.base.model.RestResponse;
import com.chaoyue.base.utils.FileUtil;
import com.chaoyue.base.utils.ObjectUtils;
import com.chaoyue.base.utils.StrUtils;
import com.chaoyue.media.beans.dto.QueryMediaParamsDto;
import com.chaoyue.media.beans.dto.UploadFileParamsDto;
import com.chaoyue.media.beans.dto.UploadFileResultDto;
import com.chaoyue.media.beans.po.MediaFiles;
import com.chaoyue.media.beans.po.MediaProcess;
import com.chaoyue.media.mapper.MediaFilesMapper;
import com.chaoyue.media.mapper.MediaProcessMapper;
import com.chaoyue.media.service.MediaFileService;
import com.chaoyue.media.utils.MinioUtils;
import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;

import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.io.*;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.chaoyue.base.constant.AuditStatus.SUBMITTED;
import static com.chaoyue.base.utils.FileUtil.getFileMd5;

@SuppressWarnings({"all"})
/**
 * @description TODO
 * @author Mr.M
 * @date 2022/9/10 8:58
 * @version 1.0
 */
@Service
@Slf4j
public class MediaFileServiceImpl implements MediaFileService {

    @Autowired
    MediaFilesMapper mediaFilesMapper;
    @Value("${minio.bucket.files}")
    private String bucket_mediafiles;
    @Value("${minio.bucket.videofiles}")
    private String bucket_video;
    @Autowired
    MediaFileService current;
    @Autowired
    MediaProcessMapper mediaProcessMapper;

    @Autowired
    private MinioUtils minioUtils;
/**
 * @Description: 根据id查询
 * @Author: whr
 * @Date: 2024/12/14 15:31
 * @Param:
 * @Return:
        */
    @Override
    public MediaFiles getFileById(String mediaId) {
        return mediaFilesMapper.selectById(mediaId);
    }
/**
 * @Description: 媒资文件分页查询
 * @Author: whr
 * @Date: 2024/12/14 15:31
 * @Param:
 * @Return:
        */
    @Override
    public PageResult<MediaFiles> queryMediaFiels(Long companyId, PageParams pageParams, QueryMediaParamsDto queryMediaParamsDto) {
        LambdaQueryWrapper<MediaFiles> queryWrapper = new LambdaQueryWrapper<>();
        Page<MediaFiles> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize());
        Page<MediaFiles> pageResult = mediaFilesMapper.selectPage(page, queryWrapper);
        List<MediaFiles> list = pageResult.getRecords();
        long total = pageResult.getTotal();
        PageResult<MediaFiles> mediaListResult = new PageResult<>(list, total, pageParams.getPageNo(), pageParams.getPageSize());
        return mediaListResult;
    }


    @Transactional
    /**
     * @description: 保存文件信息到数据库
     * @param: null
     * @return:
     * @author whr
     * @date: 2024/12/12 21:21
     */
    public MediaFiles addMediaFilesToDb(Long companyId, UploadFileParamsDto uploadFileParamsDto, String fileMd5, String bucket, String objeckName) {
        MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);
        if (ObjectUtils.isEmpty(mediaFiles)) {
            mediaFiles = new MediaFiles();
            BeanUtils.copyProperties(uploadFileParamsDto, mediaFiles);
            mediaFiles.setId(fileMd5);
            mediaFiles.setCompanyId(companyId);
            mediaFiles.setFileId(fileMd5);
            mediaFiles.setFilePath(objeckName);
            mediaFiles.setUrl("/" + bucket + "/" + objeckName);
            mediaFiles.setStatus(MediaStatus.NORMAL.getCode());
            mediaFiles.setAuditStatus(SUBMITTED.getCode());
            mediaFiles.setBucket(bucket);
            mediaFiles.setCreateDate(LocalDateTime.now());
            int insert = mediaFilesMapper.insert(mediaFiles);
            if (insert <= 0) {
                log.debug("向数据库保存数据失败 fileMd5{},bucket:{},objeckName:{}", fileMd5, bucket, objeckName);
                return null;
            }
            addWaitingTask(mediaFiles);
            return mediaFiles;
        }
        return mediaFiles;
    }

    /**
     * @description: 添加视频待处理任务
     * @param: mediaFiles
     * @return:
     * @author whr
     * @date: 2024/12/12 17:27
     */
    private void addWaitingTask(MediaFiles mediaFiles) {
        String filename = mediaFiles.getFilename();
        String extentions = filename.substring(filename.lastIndexOf("."));
        String mimeType = FileUtil.getMimeType(extentions);
        if (mimeType.equals("video/x-msvideo")) {
            MediaProcess mediaProcess = new MediaProcess();
            BeanUtils.copyProperties(mediaFiles, mediaProcess);
            mediaProcess.setStatus(MediaProcessStatus.UNPROCESSED.getCode());
            mediaProcess.setCreateDate(LocalDateTime.now());
            mediaProcess.setUrl(null);
            mediaProcess.setFailCount(0);
            mediaProcessMapper.insert(mediaProcess);
        }
    }

    /**
     * @Description: 合并分块文件并将文件信息入库
     * @Author: whr
     * @Date: 2024/12/14 15:32
     * @Param:
     * @Return:
            */
    @Override
    @Transactional
    public RestResponse<Boolean> mergechunks(Long companyId, String fileMd5, int chunkTotal, UploadFileParamsDto uploadFileParamsDto) {

        String objectName = minioUtils.mergechunks(fileMd5, chunkTotal, bucket_video, uploadFileParamsDto.getFilename());
        if (StrUtils.isEmpty(objectName)) {
            log.error("分块合并出错,bucket:{},objectName:{},错误信息:{}" + bucket_video, objectName);
            return RestResponse.validfail(false, "分块合并出错");
        }
        //下载文件进行md5校验
        File file = minioUtils.downloadFileFromMinIO(objectName, bucket_video);
        uploadFileParamsDto.setFileSize(file.length());
        try (InputStream inputStream = new FileInputStream(file)) {
            String s = DigestUtils.md5DigestAsHex(inputStream);
            if (!s.equals(fileMd5)) {
                log.error("文件校验出错,bucket:{},objectName:{},错误信息:{}" + bucket_video, objectName);
                return RestResponse.validfail(false, "文件校验出错");
            }

        } catch (Exception e) {
            log.error("文件校验出错,bucket:{},objectName:{},错误信息:{}" + bucket_video, objectName, e.getMessage());
            return RestResponse.validfail(false, "文件校验出错");
        }
        //文件信息入库
        MediaFiles mediaFiles = current.addMediaFilesToDb(companyId, uploadFileParamsDto, fileMd5, bucket_video, objectName);
        if (ObjectUtils.isEmpty(mediaFiles)) {
            return RestResponse.validfail(false, "文件信息入库失败");
        }
        //清理分块
        minioUtils.clearChunkFiles(minioUtils.getChunkFileFolderPath(fileMd5), chunkTotal);
        return RestResponse.success(true);
    }

    /**
     * @description: 上传图片
     * @param: null
     * @return:
     * @author whr
     * @date: 2023/5/12 20:46
     */

    @Override
    @Transactional
    public UploadFileResultDto uploadFile(UploadFileParamsDto uploadDto) {
        String localFilePath = uploadDto.getLocalFilePath();
        String objectName = uploadDto.getObjectName();
        String fileName = uploadDto.getFilename();
        Long companyId = uploadDto.getCompanyId();
        String extension = fileName.substring(fileName.lastIndexOf("."));
        String mimeType = FileUtil.getMimeType(extension);
        String defaultFolderPath = minioUtils.getDefaultFolderPath();
        String fileMd5 = FileUtil.getFileMd5(new File(localFilePath));
        if (StrUtils.isEmpty(objectName)) {
            objectName = defaultFolderPath + fileMd5 + extension;
        }
        //上传文件到minio
        boolean result = minioUtils.addMediaFilesToMinIO(localFilePath, mimeType, bucket_mediafiles, objectName);
        if (!result) {
            ChaoYuePlusException.cast("上传文件失败");
        }
        MediaFiles mediaFiles = current.addMediaFilesToDb(companyId, uploadDto, fileMd5, bucket_mediafiles, objectName);
        if (mediaFiles == null) {
            log.debug("上传文件信息失败,objectName{}", objectName);
            ChaoYuePlusException.cast("上传文件信息失败");
        }

        UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();
        BeanUtils.copyProperties(mediaFiles, uploadFileResultDto);
        return uploadFileResultDto;
    }

    //检查分块是否上传
    @Override
    public RestResponse<Boolean> checkChunk(String fileMd5, int chunkIndex) {
        return RestResponse.success(Boolean.TRUE.equals(minioUtils.checkChunk(fileMd5, chunkIndex, bucket_video)));
    }

    //检查文件是否上传
    @Override
    public RestResponse<Boolean> checkFile(String fileMd5) {
        MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);
        if (ObjectUtils.isEmpty(mediaFiles)) {
            return RestResponse.success(Boolean.FALSE);
        }
        String bucket = mediaFiles.getBucket();
        String filePath = mediaFiles.getFilePath();
        return RestResponse.success(Boolean.TRUE.equals(minioUtils.checkFile(bucket_video, filePath)));
    }

    //上传分块
    @Override
    public RestResponse uploadChunk(String fileMd5, int chunk, String localChunkFilePath) {
        String chunkFilePath = minioUtils.getChunkFileFolderPath(fileMd5) + chunk;
        String mimeType = FileUtil.getMimeType(null);
        boolean uploadFlag = minioUtils.addMediaFilesToMinIO(localChunkFilePath, mimeType, bucket_video, chunkFilePath);
        if (!uploadFlag) {
            return RestResponse.validfail(false, "上传分块文件失败");
        }
        return RestResponse.success(true);
    }


}
